<?php
/**
 * XmuSlh, web platform of Student Association Federation of Xiamen University
 * 
 * Rewritting one or some pages of StatusNet. 
 * To get the original version, please visit <http://status.net/>.
 * 
 * StatusNet, the distributed open-source microblogging tool
 * 
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Make the every action a html page.
 * The reason why to do such design is that every action from the browser
 * may be mapped by a specific page, so just do it!
 * It Action class is the based class of all the actions!
 * 
 * 所有___Action类的父类, 定义了基本的HTML渲染和输出方法, 同时还有缓存, 错误处理等过程. 
 * 
 * @author		ytukie <ytukie@gmail.com>
 * @version		0.1
 */

// XmuSlh only.
if ( !defined('XMUSLH')) { exit(1); }

// Requirements!
require_once INSTALLDIR . '/lib/slh_htmloutputter.php';


/**
 * Base class for all actions.
 * 
 * This is the base class for all actions in the package. An action is
 * more or less a "view" in an MVC framework.
 * Actions are responsible for extracting and validating parameters;
 * using model classes to read and write to the database; and doing output.
 */
class Action extends HTMLOutputter
{
	// What args? Globally?
	var $args;
	
	/**
	 * Constructor
	 * 
	 * None.
	 */
	function __construct($output = 'php://output', 
						 $indent = null)
	{
		parent::__construct($output, $indent);
	}
	
//////// Procedure relative /////////////////////////////////////////////////////////
	/**
	 * Has etag? (private)
	 */
	function _hasEtag($etag, $if_none_match)
	{
		$etag = explode(',', $if_none_match);
		return in_array($etag, $etags) || in_array('*', $etags);
	}
		
	/**
	 * Return last modified, if applicable, overloading.
	 */
	function lastModified()
	{
		// For comparison with If-Last-Modified
		// If not applicable, return null.
		return null;
	}
	
	/**
	 * Return etag, if applicable, overloading.
	 */
	function etag()
	{
		return null;
	}
	
	/**
	 * Return ture if read only, overloading.
	 */
	function isReadOnly($args)
	{
		return false;
	}
	
	/**
	 * Is this action cacheable?
	 * 
	 * If the action returns a last-modified
	 */
	function isCacheable()
	{
		return true;
	}
	
	/**
	 * check if the current request is a POST.
	 */
	function isPost()
	{
		return ($_SERVER['REQUEST_METHOD' == 'POST']);
	}
	
	/**
	 * Returns query argument or default value if not found.
	 */
	function getArg($key, 
					$def = null)
	{
		if (array_key_exists($key, $this->args)) {
			return $this->args[$key];
		} else {
			return $def;
		}
	}
	
	/**
	 * Returns trimmed query argument or default value if not found.
	 */
	function getTrimmedArg($key, 
						   $def = null)
	{
		$arg = $this->getArg($key, $def);
		return is_string($arg) ? trim($arg) : $arg;	
	}
	
	/**
	 * Returns arguments sfficient for re-constructing URL
	 */
	function returnToArgs()
	{
		$action = $this->getTrimmedArg('action');
		$args	= $this->args;
		unset($args['action']);
		
		if (array_key_exists('sumbit', $args)) {
			unset($args['submit']);
		}
		foreach (array_keys($_COOKIE) as $cookie) {
			unset($args[$cookie]);
		}
		
		return array($action, $args);
	}
	
	/**
	 * Returns the current URL.
	 */
	function selfUrl()
	{
		list($action, $args) = $this->returnToArgs();
// TODO: Return a current URL!
		return "index.php";
	}
	
	/**
	 * For initializing members of the class.
	 * And do some preparation like copying the args.
	 * 
	 * @return boolean true
	 */
	function prepare($argarray)
	{
// TODO: It is actually a cloning!!!		
		$this->args = $argarray;
		
		return true;
	}
	
	/**
	 * Handler method
	 */
	function handle($argarray = null)
	{
		header('Vary: Accept-Encoding,Cookie');
		
		$lm		= $this->lastModified();
		$etag	= $this->etag();
		
		if ($etag) {
			header('ETag: ' . $etag);
		}
		
		if ($lm) {
			header('Last-Modified: ' . date(DATE_RFC1123, $lm));
			if ($this->isCacheable()) {
				header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT');
				header('Cache-Contorl: private, must-revalidate, max-age=0');
				header('Pragma:');
			}
		}
		
		$checked = false;
		if ($etag) {
			$if_none_match = (array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER)) ?
				$_SERVER['HTTP_IF_NONE_MATCH'] : null;
			
			if ($if_none_match) {
				// If this check fails, ignore the if-modified-since below.
				$checked = true;
				if ($this->_hasEtag($etag, $if_none_match)) {
					header('HTTP/1.1 304 Not Modified');
// FIXME: Find a better way to cache!
					exit(0);
				}
			}
		}
		
		if ( !$checked && $lm && array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) {
			$if_modified_since = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
			$ims = strtotime($if_modified_since);
			if ($lm < $ims) {
				header('HTTP/1.1 304 Not Modified');
// FIXME: Also find a better way to cache!
				exit(0);
			}
		}
	}
	
	/** 
	 * Check the session token.
	 * 
	 * Checks that the current form has the correct session token,
	 * and throw an exception if it does not.
	 */
	function checkSessionToken()
	{
		$token = $this->getTrimmedArg('token');
// TODO: To fix it for seesion token!
	}
/////////////////////////////////////////////////////////////////////////////////////

//////// Sections relative //////////////////////////////////////////////////////////
	/**
	 * Show header of the page.
	 */
	function showHeader()
	{
		$this->elementStart('div', array('id' => 'header'));
		
		// Not actually needed currently.
		// $this->showLogo();
		// $this->showPrimaryNav();
		// $this->showSiteNotice();
		// $this->showNoticeForm();
		// $this->showAnonymousMessage();
		
		$this->elementEnd('div');
	}
	
	/**
	 * Show Footer.
	 */
	function showFooter()
	{
		$this->elementStart('div', array('id' => 'footer'));
		
		// $this->showSecondaryNav();
		// $this->showLicenses();
		
		$this->elementEnd('div');
	}
	
	/**
	 * Show core.
	 */
	function showCore()
	{
		$this->elementStart('div', array('id' => 'core'));
		
		// Not actually needed currently.
		// $this->showLocalNavBlock();
		// $this->showContentBlock();
		// $this->showAside();
		
		$this->elementEnd('div');
	}
	
	/**
	 * Show themed shortcut icon
	 */
	function showShortcutIcon()
	{
// TODO: showShortcutIcon()!
	}
	
	/**
	 * Show stylesheets.
	 * Includes the style sheets!
	 */
	function showStylesheets()
	{
// TODO: showStylesheets()!		
	}
	
	/**
	 * Add the CSS into the display!
	 */
	function primaryCssLink($mainTheme = null, 
							$media = null)
	{
// TODO: primaryCssLink()!
	}
	
	/**
	 * Show JavaScript headers!
	 */
	function showScripts()
	{
// TODO: showScripts()!
	}
	
	/**
	 * Exports a map of localized text strings to JavaScript code.
	 */
	function showScriptMessages()
	{
// TODO: showScriptMessages()!
	}
	
	/**
     * If the action will need localizable text strings, export them here like so:
     *
     * return array('pool_deepend' => _('Deep end'),
     *              'pool_shallow' => _('Shallow end'));
     *
     * The exported map will be available via SN.msg() to JS code:
     *
     *   $('#pool').html('<div class="deepend"></div><div class="shallow"></div>');
     *   $('#pool .deepend').text(SN.msg('pool_deepend'));
     *   $('#pool .shallow').text(SN.msg('pool_shallow'));
     *
     * Exports a map of localized text strings to JavaScript code.
     */
	function getScriptMessages()
	{
		return array();
	}
	
	// showOpenSearch() Discarded!
	
	// showFeeds() Discarded currently!
	function showFeeds() {}

	/**
	 * Show description, overload later!
	 */
	function showDescription()
	{
		;
	}
	
/////////////////////////////////////////////////////////////////////////////////////

//////// Tags relattive /////////////////////////////////////////////////////////////
	
	/**
	 * Show page, a templated method.
	 */
	function showPage()
	{
// FIXME: Eliminates many event handlers here.
		$this->startHTML();
		$this->showHead();
		$this->showBody();
		$this->endHTML();
	}
	
	/**
	 * Ends the HTML tags.
	 * 
	 * Do some performace detection.
	 */
	function endHTML()
	{
// TODO: $_startTime - $endTime!
		return parent::endHTML();
	}
	
	/**
	 * Show <head> element, a templated method.
	 */
	function showHead()
	{
		$this->elementStart('head');
		// Eliminated handler here.
		
		$this->showTitle();
		$this->showShortcutIcon();
		$this->showStylesheets();
		// Currently ignores the feeds!
		// $this->showFeeds();
		$this->showDescription();
		$this->extraHead();
		
		$this->elementEnd('head');
	}
	
	/**
	 * Show extra stuff in <head>, overload later?
	 */
	function extraHead()
	{
		;
	}
	
	/**
	 * Show Body! The templated design pattern.
	 */
	function showBody()
	{
// TODO: Something like user! how to show?
		$this->elementStart('body');
		
		$this->elementStart('div');
		$this->showHeader();
		$this->showCore();
		$this->showFooter();
		$this->elementEnd('div');
		$this->showScripts();
		
		$this->elementEnd('body');
	}
	
	/**
	 * Show <title> element.
	 */
	function showTitle()
	{
// TODO: Find a way to display ideal title.
		$this->element('title', 
					   null,
					   $this->title());
	}
	
	/**
	 * Returns the page title. Overload later!
	 */
	function title()
	{
		return _("Student Association Federation of Xiamen University");
	}
	
/////////////////////////////////////////////////////////////////////////////////////

//////// Others /////////////////////////////////////////////////////////////////////
	
	/**
	 * @deprecated
	 */
	function boolean($key, $def = false) {}
	
	/**
	 * @deprecated
	 */
	function ini($key, $defValue = null, $maxValue = null, $minValue = null) {}
	
	/**
	 * Server error!
	 */
	function serverError($msg, 
						 $code = 500)
	{
// TODO: Server error handle!
	}
	
	/**
	 * Client error!
	 */
	function clientError($msg, 
						 $code = 400)
	{
//  TODO: Client error handle!
	}
	
	/**
	 * An array of feeds for this action.
	 * @deprecated
	 */
	function getFeeds() {}
	
	/**
	 * A design for this action.
	 * @deprecated
	 */
	function getDesign() {}
/////////////////////////////////////////////////////////////////////////////////////
	
// TODO: Show many things specially below!
	/**
	 * Generate a menu item.
	 */
	function menuItem($url, $text, $title = null, $is_selected = false, $id = null) {}
	
	/**
	 * Generate pagination links.
	 */
	function pagination($have_before, $have_after, $page, $action, $args = null) {}
	
	/**
	 * Show configured logo.
	 */
	function showLogo() {}
	
	/**
	 * Show primary navigation.
	 */
	function showPrimaryNav() {}
	
	/**
	 * Show site notice.
	 */
	function showSiteNotice() {}
	
	/**
	 * Show notice form.
	 * 
	 * MAY overload if no notice form needed.. or direct message box.
	 */
	function showNoticeForm() {}
	
	/**
	 * Show anonymous message.
	 */
	function showAnonymousMessage() {}
	
	/**
	 * Show local navigation block.
	 */
	function showLocalNavBlock() {}
	
	/**
	 * Show local navigation, overload later.
	 */
	function showLocalNav() {}
	
	/**
	 * Show content block.
	 */
	function showContentBlock() {}
	
	/**
	 * Show page title.
	 */
	function showPageTitle() {}
	
	/**
	 * Show page notice block.
	 */
	function showPageNoticeBlock() {}
	
	/**
	 * Show page notice.
	 */
	function showPageNotice() {}
	
	/**
	 * Show content, overloads later.
	 */
	function showContent() {}
	
	/**
	 * Show aside.
	 */
	function showAside() {}
	
	/**
	 * Show export data feeds.
	 */
	function showExportData() {}
	
	/**
	 * Show sections, overloading.
	 */	
	function showSections() {}
	
	/**
	 * Show secondary navigation.
	 */
	function showSecondaryNav() {}
	
	/**
	 * Show licenses.
	 */
	function showLicenses() {}
	
	/**
	 * Show StatusNet license.
	 */
	// function showStatusNetLicense() {}
	
	/**
	 * Show content license.
	 */	
	function showContentLicence() {}
	
}